﻿/*
    Copyright 2010, 2011 Eric Wong
	contact@optimalcycling.com
	http://www.optimalcycling.com
	Optimal Cycling - Advanced power pacing program for cyclists
	
	This file is part of Optimal Cycling.

    Optimal Cycling is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Optimal Cycling is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Optimal Cycling.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.IO;
using OptimalCycling;

namespace OptimalCycling.FileIO.OC
{
    /// <summary>
    /// What type of power pacing optimization to perform.
    /// </summary> 
    public enum OptimizationChoices
    {
        NoOptimization, // Runs the simulation with the supplied power numbers only.
        OptimizeUsingLimits,    // Optimization where all power values can be different.
        OptimizeUsingLimitsAllSame  // Optimization where all power values are the same.
    }

    /// <summary>
    /// Contains the string names of the console output file and the intermediate save files.
    /// </summary> 
    public sealed class MiscFiles
    {
        public static readonly string consoleFileName = @"ConsoleOutput.txt";
        public static readonly string intermedFolderName = @"Intermed";
        public static readonly string intermedA = @"IntermedA.xml";
        public static readonly string intermedB = @"IntermedB.xml";
    }

    /// <summary>
    /// Contains the string names, default values, and limits for the simulation data columns.
    /// </summary> 
    public sealed class DataFiles
    {
        public const uint MAX_ROWS = 10000;

        public static readonly TableInfo fileInformation = new TableInfo(FileIOConstants.FILE_INFORMATION, FileInfoColNames.dataColInfo, 1, false, false, false);
        public static readonly TableInfo comments = new TableInfo(@"Comments.csv", CommentsColNames.dataColInfo, 128, true, true, false);
        public static readonly TableInfo[] tableInfoFile = { fileInformation, comments };
        public static readonly TableGroupInfo groupFile = new TableGroupInfo("File Info.", tableInfoFile);

        public static readonly TableInfo course = new TableInfo(@"Course.csv", CourseColNames.dataColInfo, MAX_ROWS, true, true, false);
        public static readonly TableInfo wheelDragCoeff = new TableInfo(@"WheelDragCoeff.csv", WheelDragCoeffColNames.dataColInfo, MAX_ROWS, true, true, false);
        public static readonly TableInfo wheelRotInertia = new TableInfo(@"WheelRotInertia.csv", WheelRotInertiaColNames.dataColInfo, MAX_ROWS, true, true, false);
        public static readonly TableInfo miscModelData = new TableInfo(@"MiscModelData.csv", MiscModelDataColNames.dataColInfo, 1, false, false, false);
        public static readonly TableInfo[] tableInfoModelData = { course, wheelDragCoeff, wheelRotInertia, miscModelData };
        public static readonly TableGroupInfo groupModelData = new TableGroupInfo("Model Data", tableInfoModelData);

        public static readonly TableInfo powerMetricsParams = new TableInfo(@"PowerMetricsParams.csv", PowerMetricsParamsColNames.dataColInfo, 1, false, false, false);
        public static readonly TableInfo powerMetricsCriticals = new TableInfo(@"PowerMetricsCriticals.csv", PowerMetricsCriticalsColNames.dataColInfo, MAX_ROWS, true, true, false);
        public static readonly TableInfo[] tableInfoPowerMetrics = { powerMetricsParams, powerMetricsCriticals };
        public static readonly TableGroupInfo groupPowerMetrics = new TableGroupInfo("Power Metrics", tableInfoPowerMetrics);

        public static readonly TableInfo optimizationParams = new TableInfo(@"OptimizationParams.csv", OptimizationParamsColNames.dataColInfo, 1, false, false, false);
        public static readonly TableInfo integrationParams = new TableInfo(@"IntegrationParams.csv", IntegrationParamsColNames.dataColInfo, 1, false, false, false);       
        public static readonly TableInfo[] tableInfoOptimizations = { optimizationParams, integrationParams};
        public static readonly TableGroupInfo groupOptimizations = new TableGroupInfo("Optimizations", tableInfoOptimizations);

        public static readonly TableInfo resultsCourse = new TableInfo(@"ResultsCourse.csv", ResultsCourseColNames.dataColInfo, uint.MaxValue, false, false, false);
        public static readonly TableInfo resultsPowerMetrics = new TableInfo(@"ResultsPowerMetrics.csv", ResultsPowerMetricColNames.dataColInfo, uint.MaxValue, false, false, false);
        public static readonly TableInfo[] tableInfoResults = { resultsCourse, resultsPowerMetrics };
        public static readonly TableGroupInfo groupResults = new TableGroupInfo("Results", tableInfoResults);

        public static readonly TableGroupInfo[] tableGroupInfo = { groupFile, groupModelData, groupPowerMetrics, groupOptimizations, groupResults };
    }

    /// <summary>
    /// Stores the file version as a string to let us know what type of data we are dealing with.
    /// </summary> 
    public sealed class FileInfoColNames
    {
        public static readonly DataColInfo fileVersion = new DataColInfo(@"File Version", typeof(string), 
            OptimalCyclingFileFormat.OptimalCyclingDataV2_1.ToString(), null, null, false);

        public static readonly DataColInfo[] dataColInfo = { fileVersion };
    }

    /// <summary>
    /// Allow the end user to write some comments into the data.
    /// </summary> 
    public sealed class CommentsColNames
    {
        public static readonly DataColInfo comments0 = new DataColInfo(@"Comments 0", typeof(string), @"", null, null, false);
        public static readonly DataColInfo comments1 = new DataColInfo(@"Comments 1", typeof(string), @"", null, null, false);
        public static readonly DataColInfo comments2 = new DataColInfo(@"Comments 2", typeof(string), @"", null, null, false);
        public static readonly DataColInfo comments3 = new DataColInfo(@"Comments 3", typeof(string), @"", null, null, false);
        public static readonly DataColInfo comments4 = new DataColInfo(@"Comments 4", typeof(string), @"", null, null, false);

        public static readonly DataColInfo[] dataColInfo = { comments0, comments1, comments2, comments3, comments4 };
    }

    /// <summary>
    /// Contains the main parameters that a user would change to alter the race course and power limits.
    /// </summary> 
    public sealed class CourseColNames
    {
        public static readonly DataColInfo courseX = new DataColInfo(@"Course X [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo courseY = new DataColInfo(@"Course Y [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo courseZ = new DataColInfo(@"Course Z [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo windX = new DataColInfo(@"Wind X [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo windY = new DataColInfo(@"Wind Y [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo windZ = new DataColInfo(@"Wind Z [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo power = new DataColInfo(@"Power [watts]", typeof(double), 300.0, 0.0, 3000.0, false);
        public static readonly DataColInfo powerMin = new DataColInfo(@"Power Minimum [watts]", typeof(double), 0.0, 0.0, 3000.0, false);
        public static readonly DataColInfo powerMax = new DataColInfo(@"Power Maximum [watts]", typeof(double), 1800.0, 0.0, 3000.0, false);

        public static readonly DataColInfo speedLimitHigh = new DataColInfo(@"Speed Limit High [m/s]", typeof(double), 100.0, 0.0, 100.0, false);

        public static readonly DataColInfo airTemperature = new DataColInfo(@"Air Temperature [Celsius]", typeof(double), 25.0, -40.0, 60.0, false);
        public static readonly DataColInfo dewPointTemperature = new DataColInfo(@"Dew Point Temperature [Celsius]", typeof(double), 0.0, -40.0, 60.0, false);
        public static readonly DataColInfo airPressure = new DataColInfo(@"Air Pressure [Pascals]", typeof(double), 101325, 0.0, 1E6, false);
        public static readonly DataColInfo generalCdA = new DataColInfo(@"General CdA [m^2]", typeof(double), 0.25, 0.01, 5.0, false);

        public static readonly DataColInfo crr = new DataColInfo(@"Crr [unitless]", typeof(double), 0.004, 0.0, 1.0, false);
        public static readonly DataColInfo massOfRider = new DataColInfo(@"Mass of Rider [kg]", typeof(double), 70.0, 0.0, 200.0, false);
        public static readonly DataColInfo massFrontWheel = new DataColInfo(@"Mass Front Wheel [kg]", typeof(double), 1.0, 0.0, 20.0, false);
        public static readonly DataColInfo massRearWheel = new DataColInfo(@"Mass Rear Wheel [kg]", typeof(double), 1.5, 0.0, 20.0, false);
        public static readonly DataColInfo massBikeWithoutWheels = new DataColInfo(@"Mass Bike Without Wheels [kg]", typeof(double), 6.0, 0.0, 50.0, false);

        public static readonly DataColInfo[] dataColInfo = { courseX, courseY, courseZ, windX, windY, windZ, power, powerMin, powerMax, speedLimitHigh,
                                                           airTemperature, dewPointTemperature, airPressure, generalCdA, crr, massOfRider,
                                                           massFrontWheel, massRearWheel, massBikeWithoutWheels};
    }

    /// <summary>
    /// Contains the data for the yaw angle and the respective drag coefficients for the wheels.
    /// </summary> 
    public sealed class WheelDragCoeffColNames
    {
        public static readonly DataColInfo yawAngle = new DataColInfo(@"Yaw Angle [degrees]", typeof(double), 0.0, -180.0, 180.0, true);
        public static readonly DataColInfo frontWheelDragCoeff = new DataColInfo(@"Front Wheel Drag Coefficient [unitless]", typeof(double), 0.04, -2.0, 2.0, false);
        public static readonly DataColInfo rearWheelDragCoeff = new DataColInfo(@"Rear Wheel Drag Coefficient [unitless]", typeof(double), 0.04, -2.0, 2.0, false);

        public static readonly DataColInfo[] dataColInfo = { yawAngle, frontWheelDragCoeff, rearWheelDragCoeff };
    }

    /// <summary>
    /// Contains the data for the rotational inertia of the wheels as function of speed.
    /// </summary> 
    public sealed class WheelRotInertiaColNames
    {
        public static readonly DataColInfo speed = new DataColInfo(@"Speed [m/s]", typeof(double), 0.0, 0.0, 100.0, true);
        public static readonly DataColInfo frontWheelRotInertia = new DataColInfo(@"Front Wheel Rot. Inertia [kg*m^2]", typeof(double), 0.0885, 0.0, 1000.0, false);
        public static readonly DataColInfo rearWheelRotInertia = new DataColInfo(@"Rear Wheel Rot. Inertia [kg*m^2]", typeof(double), 0.125, 0.0, 1000.0, false);

        public static readonly DataColInfo[] dataColInfo = { speed, frontWheelRotInertia, rearWheelRotInertia };
    }

    /// <summary>
    /// Contains various model parameters that only require a single value.
    /// </summary> 
    public sealed class MiscModelDataColNames
    {
        public static readonly DataColInfo driveTrainEfficiency = new DataColInfo(@"Drive Train Efficiency [0-1]", typeof(double), 0.98, 0.0, 1.0, false);
        public static readonly DataColInfo frontWheelDiameter = new DataColInfo(@"Front Wheel Diameter [m]", typeof(double), 0.674, 0.01, 10.0, false);
        public static readonly DataColInfo rearWheelDiameter = new DataColInfo(@"Rear Wheel Diameter [m]", typeof(double), 0.674, 0.01, 10.0, false);
        public static readonly DataColInfo rearWheelShieldingFactor = new DataColInfo(@"Rear Wheel Shielding Drag Reduction Factor [unitless]", typeof(double), 
            0.25, 0.0, 1.0, false);

        public static readonly DataColInfo gravityCoeff = new DataColInfo(@"Gravity Coeff. [m/s^2]", typeof(double), 9.80665, 9.0, 11.0, false);

        public static readonly DataColInfo[] dataColInfo = { driveTrainEfficiency, frontWheelDiameter, rearWheelDiameter, rearWheelShieldingFactor,
                                                           gravityCoeff};
    }

    /// <summary>
    /// Contains the parameters that govern how efficiently the solver runs when optimization the simulation.
    /// </summary> 
    public sealed class OptimizationParamsColNames
    {
        public const uint MAX_NUM_POPULATIONS = 100;
        public const uint MAX_POPULATION_SIZE = 100000;
        public const ulong MAX_GENS_TO_RUN = (ulong)(1E10);

        public static readonly DataColInfo progressUpdateFrequency = new DataColInfo(@"Progress Update Frequency [>=0]", typeof(ulong), 10, 0, MAX_GENS_TO_RUN, false);
        public static readonly DataColInfo intermedSaveFrequency = new DataColInfo(@"Intermediate Save Frequency [>=0]", typeof(ulong), 500, 0, MAX_GENS_TO_RUN, false);
        public static readonly DataColInfo keepAllIntermed = new DataColInfo(@"Keep All Intermediates [bool]", typeof(bool), false, null, null, false);

        public static readonly DataColInfo randNumGenSeeds =
            new DataColInfo(@"Rand Num. Gen. Seed", typeof(int), 1234567890, int.MinValue, int.MaxValue, false);

        public static readonly DataColInfo numPopulations = new DataColInfo(@"Num. Populations [>=1]", typeof(uint), 1, 1, MAX_NUM_POPULATIONS, false);
        public static readonly DataColInfo populationSize = new DataColInfo(@"Population Size [>=10]", typeof(uint), 50, 10, MAX_POPULATION_SIZE, false);
        public static readonly DataColInfo maxGensToRun = new DataColInfo(@"Max. Gens. to Run [>=1]", typeof(ulong), 10000, 1, MAX_GENS_TO_RUN, false);
        public static readonly DataColInfo gensBetweenMigrations = new DataColInfo(@"Gens. Between Migrations [>=1]", typeof(ulong), 1000, 1, MAX_GENS_TO_RUN, false);
        public static readonly DataColInfo migrationSize = new DataColInfo(@"Migration Size [>=1]", typeof(uint), 25, 1, MAX_POPULATION_SIZE, false);

        public static readonly DataColInfo maxHeuristicSeeds = new DataColInfo(@"Max. Num. Heuristic Seeds [>=0]", typeof(uint), 25, 0, MAX_POPULATION_SIZE, false);

        public static readonly DataColInfo convergenenceGenerations = new DataColInfo(@"Convergence Generations [>=1]", typeof(ulong), 500, 1, MAX_GENS_TO_RUN, false);
        public static readonly DataColInfo convergenceTolerance = new DataColInfo(@"Convergence Tolerance [>=0]", typeof(double), 0.001, 0.0, float.MaxValue, false);

        public static readonly DataColInfo[] dataColInfo = { progressUpdateFrequency, intermedSaveFrequency, keepAllIntermed,
                                                           randNumGenSeeds, numPopulations,
                                                           populationSize, maxGensToRun, gensBetweenMigrations, migrationSize, 
                                                           maxHeuristicSeeds,
                                                           convergenenceGenerations, convergenceTolerance};
    }

    /// <summary>
    /// Contains the parameters that govern how accurate the numerical integration is.
    /// </summary> 
    public sealed class IntegrationParamsColNames
    {
        public const double MAX_TIME = 200000;

        public static readonly DataColInfo minSpeed = new DataColInfo(@"Minimum Speed [m/s]", typeof(double), 0.01, 0.0001, 100.0, false);
        public static readonly DataColInfo initialSpeed = new DataColInfo(@"Initial Speed [m/s]", typeof(double), 0.02, 0.0001, 100, false);
        public static readonly DataColInfo raceTimeMeasureStartIndex = new DataColInfo(@"Race Time Measure Start Index [0-(Total Points - 1)]", typeof(uint), 0, 0, uint.MaxValue, false);
        public static readonly DataColInfo maxTimeExpected = new DataColInfo(@"Max. Time Expected [sec]", typeof(double), 10000.0, 1.0, MAX_TIME, false);
        public static readonly DataColInfo integrationInitialStepSize = new DataColInfo(@"Integration Initial Step Size [sec]", typeof(double), 0.005, 1E-6, 1.0, false);
        public static readonly DataColInfo integrationGlobalTol = new DataColInfo(@"Integration Global Tolerance [sec]", typeof(double), 0.001, 1E-6, 100.0, false);
        public static readonly DataColInfo integrationMinStepSize = new DataColInfo(@"Integration Minimum Step Size [sec]", typeof(double), 0.001, 1E-6, 1.0, false);
        public static readonly DataColInfo integrationMaxStepSize = new DataColInfo(@"Integration Maximum Step Size [sec]", typeof(double), 1, 1E-6, 10.0, false);

        public static readonly DataColInfo[] dataColInfo = { minSpeed, initialSpeed, raceTimeMeasureStartIndex,
                                                           maxTimeExpected, integrationInitialStepSize, integrationGlobalTol,
                                                           integrationMinStepSize, integrationMaxStepSize };
    }

    /// <summary>
    /// Contains the parameters used to calculate the power metrics given a power output.
    /// </summary> 
    public sealed class PowerMetricsParamsColNames
    {
#if CCAP_RATE_TRANSFORMS
        public static readonly DataColInfo posFactor = new DataColInfo(@"Positive Factor [unitless]", typeof(double), 1.0, -1000.0, 1000.0, false);
        public static readonly DataColInfo maxPosPowerRate = new DataColInfo(@"Max. Positive Power Rate [watts/sec]", typeof(double), 1000.0, 0.0, 1000000.0, false);
        public static readonly DataColInfo negFactor = new DataColInfo(@"Negative Factor [unitless]", typeof(double), -1.0, -1000.0, 1000.0, false);
        public static readonly DataColInfo minNegPowerRate = new DataColInfo(@"Min. Negative Power Rate [watts/sec]", typeof(double), -1000.0, -1000000.0, 0.0, false);
#endif

        public static readonly DataColInfo powerRateCoeff = new DataColInfo(@"Power Rate Coeff. [unitless]", typeof(double), 1.0, 0.0, 5.0, false);
        public static readonly DataColInfo powerRateExp = new DataColInfo(@"Power Rate Exp. [unitless]", typeof(double), 2.0, 0.01, 5.0, false);
        public static readonly DataColInfo powerCurveExp = new DataColInfo(@"Power Curve Exp. [unitless]", typeof(double), 1.0, 0.01, 5.0, false);
        public static readonly DataColInfo ftp = new DataColInfo(@"Functional Threshold Power [watts]", typeof(double), 200.0, 1.0, 1000.0, false);

        public static readonly DataColInfo gmExp = new DataColInfo(@"General Mean Exp. [unitless]", typeof(double), 4.0, 0.01, 5.0, false);
        public static readonly DataColInfo gmRollingAveTime = new DataColInfo(@"General Mean Rolling Average Time [sec]", typeof(double), 30.0, 0.0, 3600.0, false);
        public static readonly DataColInfo gmRollingAveIsExp = new DataColInfo(@"General Mean Rolling Average is Exponential [bool]", typeof(bool), false, null, null, false);
        public static readonly DataColInfo gmSamplingInterval = new DataColInfo(@"General Mean Sampling Interval [sec]", typeof(double), 0.25, 0.1, 10.0, false);

        public static readonly DataColInfo powerMetricChoice = new DataColInfo(@"Power Metric Choice [0-CCAP, 1-Ave, 2-GM]", typeof(PMTypes),
            PMTypes.PMTypes_CCAP, null, null, false);

        public static readonly DataColInfo powerOptimizationType = new DataColInfo(@"Power Optimization Type [0-None, 1-Limits, 2-Constant]", typeof(OptimizationChoices),
                    OptimizationChoices.OptimizeUsingLimits, null, null, false);

        public static readonly DataColInfo[] dataColInfo = { 
#if CCAP_RATE_TRANSFORMS
                                                           posFactor, maxPosPowerRate, negFactor, minNegPowerRate,
#endif
                                                           powerRateCoeff, powerRateExp, powerCurveExp, ftp,
                                                           gmExp, gmRollingAveTime, gmRollingAveIsExp, 
                                                           gmSamplingInterval, powerMetricChoice, powerOptimizationType };

    }

    /// <summary>
    /// Contains the data that represent the maximal effort an athlete can give as a function of time.
    /// </summary> 
    public sealed class PowerMetricsCriticalsColNames
    {
        public static readonly DataColInfo duration = new DataColInfo(@"Duration [sec]", typeof(double), 0.0, 0.0, float.MaxValue, false);
        public static readonly DataColInfo maxPowerMetricVal = new DataColInfo(@"Max. Power [watts]", typeof(double), 400.0, 1.0, float.MaxValue, false);

        public static readonly DataColInfo[] dataColInfo = { duration, maxPowerMetricVal };
    }

    /// <summary>
    /// Contains the results of the simulation for items such as speed and optimized power output.
    /// </summary> 
    public sealed class ResultsCourseColNames
    {
        public static readonly DataColInfo courseX = new DataColInfo(@"Course X [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo courseY = new DataColInfo(@"Course Y [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo courseZ = new DataColInfo(@"Course Z [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo linearPosition = new DataColInfo(@"Linear Position [m]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo windX = new DataColInfo(@"Wind X [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo windY = new DataColInfo(@"Wind Y [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo windZ = new DataColInfo(@"Wind Z [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo time = new DataColInfo(@"Time [s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo powerRaw = new DataColInfo(@"Optimized Power [watts]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo speed = new DataColInfo(@"Speed [m/s]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo acceleration = new DataColInfo(@"Acceleration [m/s^2]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo airDensity = new DataColInfo(@"Air Density [kg/m^3]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo[] dataColInfo = { courseX, courseY, courseZ, linearPosition,
                                                           windX, windY, windZ,
                                                           time, powerRaw, speed, acceleration, airDensity};
    }

    /// <summary>
    /// Contains the results of the simulation for the power metrics of the optimized power output.
    /// </summary> 
    public sealed class ResultsPowerMetricColNames
    {
        public static readonly DataColInfo raceTime = new DataColInfo(@"Race Time [sec]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo totalTime = new DataColInfo(@"Total Time [sec]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo aveSpeed = new DataColInfo(@"Average Speed [km/hr]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo energyUsed = new DataColInfo(@"Energy Used [J]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo avePower = new DataColInfo(@"Average Power [watts]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo ccap = new DataColInfo(@"CCAP [watts]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo gm = new DataColInfo(@"General Mean [watts]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);
        public static readonly DataColInfo pmCritical = new DataColInfo(@"Critical Power [watts]", typeof(double), 0.0, float.MinValue, float.MaxValue, false);

        public static readonly DataColInfo[] dataColInfo = { raceTime, totalTime, aveSpeed, energyUsed, avePower, ccap, gm, pmCritical };
    }

}
